home *** CD-ROM | disk | FTP | other *** search
/ PC go! 2008 October / PCgo 2008-10 (DVD).iso / interface / contents / vollversionen_6617 / 21733 / files / xulrunner / components / nsAddonRepository.js < prev    next >
Encoding:
Text File  |  2008-08-20  |  11.4 KB  |  349 lines

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /*
  3. //@line 38 "e:\builds\tinderbox\XR-Trunk\WINNT_5.2_Depend\mozilla\toolkit\mozapps\extensions\src\nsAddonRepository.js"
  4. */
  5.  
  6. const Cc = Components.classes;
  7. const Ci = Components.interfaces;
  8. const Cr = Components.results;
  9.  
  10. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  11.  
  12. const PREF_GETADDONS_BROWSEADDONS        = "extensions.getAddons.browseAddons";
  13. const PREF_GETADDONS_BROWSERECOMMENDED   = "extensions.getAddons.recommended.browseURL";
  14. const PREF_GETADDONS_GETRECOMMENDED      = "extensions.getAddons.recommended.url";
  15. const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL";
  16. const PREF_GETADDONS_GETSEARCHRESULTS    = "extensions.getAddons.search.url";
  17.  
  18. const XMLURI_PARSE_ERROR  = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
  19.  
  20. const API_VERSION = "1.1";
  21.  
  22. function AddonSearchResult() {
  23. }
  24.  
  25. AddonSearchResult.prototype = {
  26.   id: null,
  27.   name: null,
  28.   version: null,
  29.   summary: null,
  30.   description: null,
  31.   rating: null,
  32.   iconURL: null,
  33.   thumbnailURL: null,
  34.   homepageURL: null,
  35.   eula: null,
  36.   type: null,
  37.   xpiURL: null,
  38.   xpiHash: null,
  39.  
  40.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonSearchResult])
  41. }
  42.  
  43. function AddonRepository() {
  44. }
  45.  
  46. AddonRepository.prototype = {
  47.   // The current set of results
  48.   _addons: null,
  49.  
  50.   // Whether we are currently searching or not
  51.   _searching: false,
  52.  
  53.   // Is this a search for recommended add-ons
  54.   _recommended: false,
  55.  
  56.   // XHR associated with the current request
  57.   _request: null,
  58.  
  59.   // Callback object to notify on completion
  60.   _callback: null,
  61.  
  62.   // Maximum number of results to return
  63.   _maxResults: null,
  64.  
  65.   get homepageURL() {
  66.     return Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  67.                      .getService(Components.interfaces.nsIURLFormatter)
  68.                      .formatURLPref(PREF_GETADDONS_BROWSEADDONS);
  69.   },
  70.  
  71.   get isSearching() {
  72.     return this._searching;
  73.   },
  74.  
  75.   getRecommendedURL: function() {
  76.     var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  77.                          .getService(Components.interfaces.nsIURLFormatter);
  78.  
  79.     return urlf.formatURLPref(PREF_GETADDONS_BROWSERECOMMENDED);
  80.   },
  81.  
  82.   getSearchURL: function(aSearchTerms) {
  83.     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  84.                           .getService(Components.interfaces.nsIPrefBranch);
  85.     var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  86.                          .getService(Components.interfaces.nsIURLFormatter);
  87.  
  88.     var url = prefs.getCharPref(PREF_GETADDONS_BROWSESEARCHRESULTS);
  89.     url = url.replace(/%TERMS%/g, encodeURIComponent(aSearchTerms));
  90.     return urlf.formatURL(url);
  91.   },
  92.  
  93.   cancelSearch: function() {
  94.     this._searching = false;
  95.     if (this._request) {
  96.       this._request.abort();
  97.       this._request = null;
  98.     }
  99.     this._callback = null;
  100.     this._addons = null;
  101.   },
  102.  
  103.   retrieveRecommendedAddons: function(aMaxResults, aCallback) {
  104.     if (this._searching)
  105.       return;
  106.  
  107.     this._searching = true;
  108.     this._addons = [];
  109.     this._callback = aCallback;
  110.     this._recommended = true;
  111.     this._maxResults = aMaxResults;
  112.  
  113.     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  114.                           .getService(Components.interfaces.nsIPrefBranch);
  115.     var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  116.                          .getService(Components.interfaces.nsIURLFormatter);
  117.  
  118.     var uri = prefs.getCharPref(PREF_GETADDONS_GETRECOMMENDED);
  119.     uri = uri.replace(/%API_VERSION%/g, API_VERSION);
  120.     uri = urlf.formatURL(uri);
  121.     this._loadList(uri);
  122.   },
  123.  
  124.   searchAddons: function(aSearchTerms, aMaxResults, aCallback) {
  125.     if (this._searching)
  126.       return;
  127.  
  128.     this._searching = true;
  129.     this._addons = [];
  130.     this._callback = aCallback;
  131.     this._recommended = false;
  132.     this._maxResults = aMaxResults;
  133.  
  134.     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  135.                           .getService(Components.interfaces.nsIPrefBranch);
  136.     var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
  137.                          .getService(Components.interfaces.nsIURLFormatter);
  138.  
  139.     var uri = prefs.getCharPref(PREF_GETADDONS_GETSEARCHRESULTS);
  140.     uri = uri.replace(/%API_VERSION%/g, API_VERSION);
  141.     uri = uri.replace(/%TERMS%/g, encodeURIComponent(aSearchTerms));
  142.     uri = urlf.formatURL(uri);
  143.     this._loadList(uri);
  144.   },
  145.  
  146.   // Posts results to the callback
  147.   _reportSuccess: function(aCount) {
  148.     this._searching = false;
  149.     this._request = null;
  150.     // The callback may want to trigger a new search so clear references early
  151.     var addons = this._addons;
  152.     var callback = this._callback;
  153.     this._callback = null;
  154.     this._addons = null;
  155.     callback.searchSucceeded(addons, addons.length, this._recommended ? -1 : aCount);
  156.   },
  157.  
  158.   // Notifies the callback of a failure
  159.   _reportFailure: function(aEvent) {
  160.     this._searching = false;
  161.     this._request = null;
  162.     // The callback may want to trigger a new search so clear references early
  163.     var callback = this._callback;
  164.     this._callback = null;
  165.     this._addons = null;
  166.     callback.searchFailed();
  167.   },
  168.  
  169.   // Parses an add-on entry from an <addon> element
  170.   _parseAddon: function(element) {
  171.     var em = Cc["@mozilla.org/extensions/manager;1"].
  172.              getService(Ci.nsIExtensionManager);
  173.     var app = Cc["@mozilla.org/xre/app-info;1"].
  174.               getService(Ci.nsIXULAppInfo).
  175.               QueryInterface(Ci.nsIXULRuntime);
  176.  
  177.     var guid = element.getElementsByTagName("guid");
  178.     if (guid.length != 1)
  179.       return;
  180.  
  181.     // Ignore add-ons already seen in the results
  182.     for (var i = 0; i < this._addons.length; i++)
  183.       if (this._addons[i].id == guid[0].textContent)
  184.         return;
  185.  
  186.     // Ignore installed add-ons
  187.     if (em.getItemForID(guid[0].textContent) != null)
  188.       return;
  189.  
  190.     // Ignore sandboxed add-ons
  191.     var status = element.getElementsByTagName("status");
  192.     // The status element has a unique id for each status type. 4 is Public.
  193.     if (status.length != 1 || status[0].getAttribute("id") != 4)
  194.       return;
  195.  
  196.     // Ignore add-ons not compatible with this OS
  197.     var os = element.getElementsByTagName("compatible_os");
  198.     // Only the version 0 schema included compatible_os if it isn't there then
  199.     // we will see os compatibility on the install elements.
  200.     if (os.length > 0) {
  201.       var compatible = false;
  202.       var i = 0;
  203.       while (i < os.length && !compatible) {
  204.         if (os[i].textContent == "ALL" || os[i].textContent == app.OS) {
  205.           compatible = true;
  206.           break;
  207.         }
  208.         i++;
  209.       }
  210.       if (!compatible)
  211.         return;
  212.     }
  213.  
  214.     // Ignore add-ons not compatible with this Application
  215.     compatible = false;
  216.     var tags = element.getElementsByTagName("compatible_applications");
  217.     if (tags.length != 1)
  218.       return;
  219.     var vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
  220.              getService(Ci.nsIVersionComparator);
  221.     var apps = tags[0].getElementsByTagName("appID");
  222.     var i = 0;
  223.     while (i < apps.length) {
  224.       if (apps[i].textContent == app.ID) {
  225.         var minversion = apps[i].parentNode.getElementsByTagName("min_version")[0].textContent;
  226.         var maxversion = apps[i].parentNode.getElementsByTagName("max_version")[0].textContent;
  227.         if ((vc.compare(minversion, app.version) > 0) ||
  228.             (vc.compare(app.version, maxversion) > 0))
  229.           return;
  230.         compatible = true;
  231.         break;
  232.       }
  233.       i++;
  234.     }
  235.     if (!compatible)
  236.       return;
  237.  
  238.     var addon = new AddonSearchResult();
  239.     addon.id = guid[0].textContent;
  240.     addon.rating = -1;
  241.     var node = element.firstChild;
  242.     while (node) {
  243.       if (node instanceof Ci.nsIDOMElement) {
  244.         switch (node.localName) {
  245.           case "name":
  246.           case "version":
  247.           case "summary":
  248.           case "description":
  249.           case "eula":
  250.             addon[node.localName] = node.textContent;
  251.             break;
  252.           case "rating":
  253.             if (node.textContent.length > 0) {
  254.               var rating = parseInt(node.textContent);
  255.               if (rating >= 0)
  256.                 addon.rating = Math.min(5, rating);
  257.             }
  258.             break;
  259.           case "thumbnail":
  260.             addon.thumbnailURL = node.textContent;
  261.             break;
  262.           case "icon":
  263.             addon.iconURL = node.textContent;
  264.             break;
  265.           case "learnmore":
  266.             addon.homepageURL = node.textContent;
  267.             break;
  268.           case "type":
  269.             // The type element has an id attribute that is the id from AMO's
  270.             // database. This doesn't match our type values to perform a mapping
  271.             if (node.getAttribute("id") == 2)
  272.               addon.type = Ci.nsIUpdateItem.TYPE_THEME;
  273.             else
  274.               addon.type = Ci.nsIUpdateItem.TYPE_EXTENSION;
  275.             break;
  276.           case "install":
  277.             // No os attribute means the xpi is compatible with any os
  278.             if (node.hasAttribute("os")) {
  279.               var os = node.getAttribute("os").toLowerCase();
  280.               // If the os is not ALL and not the current OS then ignore this xpi
  281.               if (os != "all" && os != app.OS.toLowerCase())
  282.                 break;
  283.             }
  284.             addon.xpiURL = node.textContent;
  285.             if (node.hasAttribute("hash"))
  286.               addon.xpiHash = node.getAttribute("hash");
  287.             break;
  288.         }
  289.       }
  290.       node = node.nextSibling;
  291.     }
  292.  
  293.     // Add only if there was an xpi compatible with this os
  294.     if (addon.xpiURL)
  295.       this._addons.push(addon);
  296.   },
  297.  
  298.   // Called when a single request has completed, parses out any add-ons and
  299.   // either notifies the callback or does a new request for more results
  300.   _listLoaded: function(aEvent) {
  301.     var request = aEvent.target;
  302.     var responseXML = request.responseXML;
  303.  
  304.     if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
  305.         (request.status != 200 && request.status != 0)) {
  306.       this._reportFailure();
  307.       return;
  308.     }
  309.     var elements = responseXML.documentElement.getElementsByTagName("addon");
  310.     for (var i = 0; i < elements.length; i++) {
  311.       this._parseAddon(elements[i]);
  312.  
  313.       var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  314.                             .getService(Components.interfaces.nsIPrefBranch);
  315.       if (this._addons.length == this._maxResults) {
  316.         this._reportSuccess(elements.length);
  317.         return;
  318.       }
  319.     }
  320.  
  321.     if (responseXML.documentElement.hasAttribute("total_results"))
  322.       this._reportSuccess(responseXML.documentElement.getAttribute("total_results"));
  323.     else
  324.       this._reportSuccess(elements.length);
  325.   },
  326.  
  327.   // Performs a new request for results
  328.   _loadList: function(aURI) {
  329.     this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
  330.                     createInstance(Ci.nsIXMLHttpRequest);
  331.     this._request.open("GET", aURI, true);
  332.     this._request.overrideMimeType("text/xml");
  333.  
  334.     var self = this;
  335.     this._request.onerror = function(event) { self._reportFailure(event); };
  336.     this._request.onload = function(event) { self._listLoaded(event); };
  337.     this._request.send(null);
  338.   },
  339.  
  340.   classDescription: "Addon Repository",
  341.   contractID: "@mozilla.org/extensions/addon-repository;1",
  342.   classID: Components.ID("{8eaaf524-7d6d-4f7d-ae8b-9277b324008d}"),
  343.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonRepository])
  344. }
  345.  
  346. function NSGetModule(aCompMgr, aFileSpec) {
  347.   return XPCOMUtils.generateModule([AddonRepository]);
  348. }
  349.